package com.example.clock_demo.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.clock_demo.dto.SchedulingDayDTO;
import com.example.clock_demo.dto.SchedulingMonthlyDTO;
import com.example.clock_demo.entitys.Scheduling;
import com.example.clock_demo.listener.SchedulingImportListener;
import com.example.clock_demo.mapper.SchedulingMapper;
import com.example.clock_demo.service.SchedulingService;
import com.example.clock_demo.service.ShiftService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.YearMonth;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 排班信息服务实现类
 */
@Service
public class SchedulingServiceImpl extends ServiceImpl<SchedulingMapper, Scheduling> implements SchedulingService {
    @Autowired
    private ShiftService shiftService;
    @Override
    public List<Scheduling> getSchedulingForEmployeeByDateRange(String employeeNumber, LocalDate startDate, LocalDate endDate) {
        QueryWrapper<Scheduling> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("employee_number", employeeNumber)
                .between("schedule_date", startDate, endDate)
                .orderByAsc("schedule_date");
        return baseMapper.selectList(queryWrapper);
    }

    @Override
    @Transactional
    public void importScheduling(MultipartFile file, YearMonth importMonth) throws IOException {
        SchedulingImportListener listener = new SchedulingImportListener(this, shiftService, importMonth);
        EasyExcel.read(file.getInputStream(), listener)
                .sheet()
                .headRowNumber(3)
                .doRead();
    }

    @Override
    @Transactional
    public void deleteByMonth(YearMonth month) {
        LocalDate startDate = month.atDay(1);
        LocalDate endDate = month.atEndOfMonth();

        QueryWrapper<Scheduling> queryWrapper = new QueryWrapper<>();
        queryWrapper.between("schedule_date", startDate, endDate);

        this.remove(queryWrapper);
    }

    @Override
    public IPage<SchedulingMonthlyDTO> getMonthlySchedulingPage(Long current, Long size,
                                                                String employeeName, String teamName, YearMonth scheduleMonth) {

        // 构建查询条件
        QueryWrapper<Scheduling> queryWrapper = new QueryWrapper<>();

        // 月份条件（必填）
        LocalDate startDate = scheduleMonth.atDay(1);
        LocalDate endDate = scheduleMonth.atEndOfMonth();
        queryWrapper.between("schedule_date", startDate, endDate);

        // 员工姓名模糊查询
        if (StrUtil.isNotBlank(employeeName)) {
            queryWrapper.like("employee_name", employeeName);
        }

        // 班组名称模糊查询
        if (StrUtil.isNotBlank(teamName)) {
            queryWrapper.like("team_name", teamName);
        }

        // 按员工分组，获取不重复的员工信息
        queryWrapper.select("DISTINCT employee_number, employee_name, team_name, team_code, work_location, off_work_location");
        queryWrapper.orderByAsc("team_code","employee_number");

        // 直接使用 MyBatis-Plus 的分页查询
        IPage<Scheduling> employeePage = new Page<>(current, size);
        IPage<Scheduling> employeeResult = this.page(employeePage, queryWrapper);

        // 为每个员工构建完整的排班信息
        List<SchedulingMonthlyDTO> monthlyDTOs = new ArrayList<>();

        for (Scheduling employee : employeeResult.getRecords()) {
            // 查询该员工的所有排班信息
            QueryWrapper<Scheduling> employeeQueryWrapper = new QueryWrapper<>();
            employeeQueryWrapper.eq("employee_number", employee.getEmployeeNumber());
            employeeQueryWrapper.between("schedule_date", startDate, endDate);
            employeeQueryWrapper.orderByAsc("schedule_date");

            List<Scheduling> employeeSchedulings = this.list(employeeQueryWrapper);

            // 构建月度排班DTO
            SchedulingMonthlyDTO monthlyDTO = buildMonthlyDTO(employeeSchedulings, scheduleMonth);
            if (monthlyDTO != null) {
                monthlyDTOs.add(monthlyDTO);
            }
        }

        // 构建分页结果
        IPage<SchedulingMonthlyDTO> result = new Page<>(current, size, employeeResult.getTotal());
        result.setRecords(monthlyDTOs);

        return result;
    }

    /**
     * 构建月度排班DTO
     */
    private SchedulingMonthlyDTO buildMonthlyDTO(List<Scheduling> employeeSchedulings, YearMonth scheduleMonth) {
        if (employeeSchedulings.isEmpty()) {
            return null;
        }

        // 取第一条记录的基本信息
        Scheduling first = employeeSchedulings.get(0);

        SchedulingMonthlyDTO monthlyDTO = new SchedulingMonthlyDTO();
        monthlyDTO.setEmployeeName(first.getEmployeeName());
        monthlyDTO.setEmployeeNumber(first.getEmployeeNumber());
        monthlyDTO.setTeamName(first.getTeamName());
        monthlyDTO.setTeamCode(first.getTeamCode());
        monthlyDTO.setScheduleMonth(scheduleMonth);
        monthlyDTO.setWorkLocation(first.getWorkLocation());
        monthlyDTO.setOffWorkLocation(first.getOffWorkLocation());

        // 构建每日排班详情
        List<SchedulingDayDTO> dailySchedules = employeeSchedulings.stream()
                .map(scheduling -> {
                    SchedulingDayDTO dayDTO = new SchedulingDayDTO();
                    dayDTO.setScheduleDate(scheduling.getScheduleDate());
                    dayDTO.setShiftCode(scheduling.getShiftCode());
                    dayDTO.setScheduledDurationHours(scheduling.getScheduledDurationHours());
                    return dayDTO;
                })
                .sorted(Comparator.comparing(SchedulingDayDTO::getScheduleDate))
                .collect(Collectors.toList());

        monthlyDTO.setDailySchedules(dailySchedules);

        // 计算统计信息
        monthlyDTO.setTotalWorkDays(dailySchedules.size());

        BigDecimal totalHours = dailySchedules.stream()
                .map(SchedulingDayDTO::getScheduledDurationHours)
                .filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        monthlyDTO.setTotalWorkHours(totalHours);

        return monthlyDTO;
    }

    @Override
    public void exportMonthlyScheduling(String employeeName, String teamName, YearMonth scheduleMonth, HttpServletResponse response) throws IOException {
        // 1. 设置HTTP响应头，用于文件下载
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 文件名编码，防止中文乱码
        String fileName = URLEncoder.encode(scheduleMonth.toString() + " 月度排班表", "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

        // 2. 获取所有需要导出的排班数据 (不分页)
        Map<String, List<Scheduling>> schedulingsByEmployee = getGroupedSchedulings(employeeName, teamName, scheduleMonth);

        // 3. 将所有行（包括表头）准备好
        List<List<Object>> allSheetRows = new ArrayList<>();

        // 3.1 创建并添加表头行
        allSheetRows.add(createHeaderRow());

        // 3.2 将查询到的数据转换为数据行并添加
        allSheetRows.addAll(convertGroupedDataToExcelRows(schedulingsByEmployee));

        // 4. 使用EasyExcel写入数据，但不再调用.head()方法
        EasyExcel.write(response.getOutputStream())
                .sheet("月度排班表")
                .doWrite(allSheetRows); // 直接写入包含表头和数据的所有行
    }

    /**
     * 内部方法：获取所有符合筛选条件的排班数据，并按员工工号分组
     */
    private Map<String, List<Scheduling>> getGroupedSchedulings(String employeeName, String teamName, YearMonth scheduleMonth) {
        // 构建与分页查询相同的过滤条件
        QueryWrapper<Scheduling> queryWrapper = new QueryWrapper<>();
        LocalDate startDate = scheduleMonth.atDay(1);
        LocalDate endDate = scheduleMonth.atEndOfMonth();
        queryWrapper.between("schedule_date", startDate, endDate);

        if (StrUtil.isNotBlank(employeeName)) {
            queryWrapper.like("employee_name", employeeName);
        }
        if (StrUtil.isNotBlank(teamName)) {
            queryWrapper.like("team_name", teamName);
        }
        queryWrapper.orderByAsc("team_code", "employee_number"); // 按班组和工号排序

        // 查询所有符合条件的排班记录
        List<Scheduling> allSchedulings = this.list(queryWrapper);

        // 使用LinkedHashMap保证顺序，按员工工号分组
        return allSchedulings.stream()
                .collect(Collectors.groupingBy(Scheduling::getEmployeeNumber, LinkedHashMap::new, Collectors.toList()));
    }

    /**
     * 内部方法：创建一个表头行
     * @return 包含表头字符串的列表
     */
    private List<Object> createHeaderRow() {
        List<Object> header = new ArrayList<>();
        header.add("序号");
        header.add("班组名称");
        header.add("*班组编号");
        header.add("*日期/姓名");
        header.add("*工号");

        // 添加1到31号的日期列
        for (int i = 1; i <= 31; i++) {
            header.add(String.valueOf(i));
        }

        header.add("上班地点");
        header.add("下班地点");
        header.add("工时");
        return header;
    }

    /**
     * 内部方法：将分组后的排班数据转换为Excel的数据行格式 List<List<Object>>
     */
    private List<List<Object>> convertGroupedDataToExcelRows(Map<String, List<Scheduling>> schedulingsByEmployee) {
        List<List<Object>> dataRows = new ArrayList<>();
        int sequence = 1;
        for (Map.Entry<String, List<Scheduling>> entry : schedulingsByEmployee.entrySet()) {
            List<Scheduling> employeeSchedules = entry.getValue();
            if (employeeSchedules.isEmpty()) continue;

            List<Object> row = new ArrayList<>();
            Scheduling firstRecord = employeeSchedules.get(0);

            row.add(sequence++);
            row.add(firstRecord.getTeamName());
            row.add(firstRecord.getTeamCode());
            row.add(firstRecord.getEmployeeName());
            row.add(firstRecord.getEmployeeNumber());

            // 将每日排班转换为 Map<天, 班次代码>
            Map<Integer, String> dailyShifts = employeeSchedules.stream()
                    .collect(Collectors.toMap(s -> s.getScheduleDate().getDayOfMonth(), Scheduling::getShiftCode));

            // 填充1到31号的班次数据
            for (int i = 1; i <= 31; i++) {
                row.add(dailyShifts.getOrDefault(i, "")); // 如果当天没有排班，则填空字符串
            }

            row.add(firstRecord.getWorkLocation());
            row.add(firstRecord.getOffWorkLocation());

            // 计算总工时
            BigDecimal totalHours = employeeSchedules.stream()
                    .map(Scheduling::getScheduledDurationHours)
                    .filter(Objects::nonNull)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            row.add(totalHours);

            dataRows.add(row);
        }
        return dataRows;
    }

    @Override
    @Transactional
    public void deleteByEmployeeNumbersAndMonth(List<String> employeeNumbers, YearMonth month) {

        if (employeeNumbers == null || employeeNumbers.isEmpty()) {
            return;
        }
        // 计算月份的开始和结束日期
        LocalDate startDate = month.atDay(1);
        LocalDate endDate = month.atEndOfMonth();
        // 构建删除条件
        QueryWrapper<Scheduling> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("employee_number", employeeNumbers) // 条件1：工号在给定的列表中
                .between("schedule_date", startDate, endDate); // 条件2：日期在给定的月份内
        // 执行删除操作
        this.remove(queryWrapper);
    }
}
